package io.potato.ts.service;

import io.potato.core.AppException;
import io.potato.core.CrudService;
import io.potato.core.exception.NoDataAccessRightException;
import io.potato.core.util.AssertUtils;
import io.potato.core.util.Hashs;
import io.potato.core.util.SmsUtil;
import io.potato.ts.common.Constants;
import io.potato.ts.domain.User;
import io.potato.ts.domain.ViewUser;
import io.potato.ts.repository.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;


/**
 * 用户表服务类
 * 
 * @author timl
 *
 * <p>2019-01-07 15:13:58</p>
 */
@Service
@Transactional
public class UserService extends CrudService<User, Integer> {

	@Autowired
	UserRepository repository;
	
	@Autowired
	ViewUserRepository viewUserRepository;
	
	@Autowired
	ContactsRepository contactsRepository;

	@Autowired
	ContactsGroupRepository contactsGroupRepository;

	@Autowired
	ContactsLinkRepository contactsLinkRepository;

	@Autowired
	MobileFileRepository mobileFileRepository;

	@Autowired
	UserGroupRepository userGroupRepository;

	@Autowired
	UserGroupLinkRepository userGroupLinkRepository;

	@Autowired
    private NamedParameterJdbcTemplate namedJdbcTemplate;


    @Override
	protected JpaRepository<User, Integer> getRepository() {
		return repository;
	}
	
	/**
	 * 根据姓名手机号查找用户
	 * @param pageable  分页排序信息
	 * @param field  过滤的字段
	 * @param keyword  过滤关键字
	 * @param organId 单位ID， null 或者《=0 不过滤单位
	 * @return
	 */
	public Page<ViewUser> findByKeyword(Pageable pageable, String field, String keyword, Integer organId) {
		
		ViewUser obj = new ViewUser();
		
		if (organId != null && organId > 0) {
			obj.setOrganId(organId);
		}

		if (StringUtils.isNotEmpty(field) && StringUtils.isNotEmpty(keyword)) {
			if ("userCode".equals(field)) {
				obj.setUserCode(keyword);
			} else if ("userName".equals(field)) {
				obj.setUserName(keyword);
			} else {
				obj.setRealName(keyword);
			}
			
			
			ExampleMatcher exampleMatcher = ExampleMatcher.matching()
					.withMatcher(field, GenericPropertyMatchers.contains());
			
			return viewUserRepository.findAll(Example.of(obj, exampleMatcher), pageable);
		} else {
			return viewUserRepository.findAll(Example.of(obj), pageable);
		}
	}

	/**
	 * 查询单位下的所有用户
	 * @param organId 单位ID
	 * @return
	 */
	public List<User> findByOrganId(Integer organId) {
		return this.repository.findByOrganIdOrderByRealNameAsc(organId);
	}
	
	/**
	 * 根据用户账号查询
	 * @param userCode 用户账号(拓展码)
	 * @return  用户信息
	 */
	public User findByUserCode(String userCode) {
		return this.repository.findByUserCode(userCode);
	}
	
	/**
	 * 根据用户名查询用户
	 * @param userName  用户名
	 * @return  用户信息
	 */
	public User findByUserName(String userName) {
		return this.repository.findByUserName(userName);
	}
	
	public String findContractName(Integer userId, String mobileNo) {
		String name = this.repository.findNameByMobileNo(mobileNo);
		if (name == null) {
			name = this.contactsRepository.findNameByMobileNo(userId, mobileNo);
		}
		return name == null ? mobileNo : name;
	}
	
	/**
	 * 返回用户的角色名
	 * @param user  用户信息
	 * @return
	 */
	public String getRoleName(User user) {
		if (Constants.Role.SUPERADMIN.equals(user.getRole())) {
			return Constants.Role.NAME_SUPERADMIN;
		} else if (Constants.Role.ADMIN.equals(user.getRole())) {
			return Constants.Role.NAME_ADMIN;
		} else {
			return Constants.Role.NAME_USER;
		}
	}
	
	public User save(User user, Integer organId) {
		if (user.getId() == null || user.getId() <= 0) {
			this.setPassword(user, user.getPassword());			
			return super.save(user);
		} else {
			Optional<User> oUser = this.findById(user.getId());
			if (oUser.isPresent()) {
				User u = oUser.get();

				if (organId != null && !organId.equals(u.getOrganId())) {
					throw new NoDataAccessRightException("无权限进行此操作");
				}

				if (organId == null) {
					if (user.getRole() != null) {
						u.setRole(user.getRole());
					}
					if (user.getOrganId() != null) {
						u.setOrganId(user.getOrganId());
					}
					if (user.getSmsLimitNum() != null) {
						u.setSmsLimitNum(user.getSmsLimitNum());
					}
				}

				u.setUserName(user.getUserName());
				u.setRealName(user.getRealName());
				u.setMobileNo(user.getMobileNo());
				u.setGender(user.getGender());
				u.setBirthday(user.getBirthday());
				u.setOfficePhone(user.getOfficePhone());
				u.setEmail(user.getEmail());
				if (user.getDeptId() != null) {
					u.setDeptId(user.getDeptId());
				}
				u.setSmsSign(user.getSmsSign());
				return super.save(u);
			} else {
				throw new AppException("用户不存在");
			}
		}
	}

	@Transactional(propagation = Propagation.REQUIRED)
	public void deleteById(Integer id, Integer organId) {

		Optional<User> optionalUser = repository.findById(id);
		if (!optionalUser.isPresent()) {
			return;
		}

		User user = optionalUser.get();

		if (organId != null && !organId.equals(user.getOrganId())) {
			throw new NoDataAccessRightException("没有权限进行此操作");
		}

		repository.deleteById(id);

		contactsRepository.deleteByUserId(id);
		contactsGroupRepository.deleteByUserId(id);
		contactsLinkRepository.deleteByUserId(id);
		mobileFileRepository.deleteByUserId(id);

		userGroupRepository.deleteByUserId(id);
		userGroupLinkRepository.deleteByUserId(id);
	}

	/**
	 * 根据主键批量删除记录
	 *
	 * param ids 主键集
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void deleteBatch(Integer[] ids, Integer organId){
		for(Integer id : ids){
			deleteById(id, organId);
		}
	}

	public void batchInsert(List<User> list) {
	    String sql = "INSERT INTO t_user(id, user_code, user_name, real_name, " +
                "password, sms_limit_by, sms_limit_num, parent_code," +
                "mobile_no, gender, office_phone, email) values(:id, :userCode, :userName, :realName, " +
                ":password, :smsLimitBy, :smsLimitNum, :parentCode," +
                ":mobileNo, :gender, :officePhone, :email)";

        BeanPropertySqlParameterSource[] batchParam = new BeanPropertySqlParameterSource[list.size()];
        for (int i = 0; i < batchParam.length; i++) {
            batchParam[i] = new BeanPropertySqlParameterSource(list.get(i));
        }

        namedJdbcTemplate.batchUpdate(sql, batchParam);
    }
	
	
	@Cacheable(value = Constants.CACHE_CONFIG)
	public Integer findIdByCode(String userCode) {
		return this.repository.findIdByCode(userCode);
	}

    @Cacheable(value = Constants.CACHE_CONFIG)
	public String findSignByCode(String userCode) {
	    String sign = repository.findSignByCode(userCode);
	    return sign == null ? " " : sign;
    }
	
	/**
	 * 验证用户名密码是否合法 （数据会缓存）
	* 只验证API接口用户， web用户返回失败
 	 * 注意， 这里使用了缓存， 也就是说在一定时间内不用再次数据库验证用户
	 * @param userCode  用户名
	 * @param password  密码
	 * @return  是否合法用户
	 */
	@Cacheable(value = Constants.CACHE_API_SESSION)
	public boolean checkUserPassword(String userCode, String password) {
		if (SmsUtil.isWebUser(userCode)) {
			return false;
		}
		
		User user = this.findByUserCode(userCode);
		if (user == null) {
			return false;
		}
		
		return Hashs.hashEquals(user.getPassword(), password);
	}
	
	private void setPassword(User user, String password) {
		if (StringUtils.isEmpty(password)) {
			return;
		}
		user.setPasswordSalt(Hashs.randomSalt());
		user.setPassword(Hashs.computeHash(password));
	}
	
	
	/**
	 * 更新用户的锁定状态
	 * @param ids  用户ID 列表
	 * @param locked  是否锁定  0 否， 1 是
	 */
	public void updateLockStatus(List<Integer> ids, Integer locked) {
		repository.updateLockStatus(ids, locked);
	}
	
	/**
	 * 更新用户短信签名
	 * @param id  用户ID
	 * @param smsSign  短信签名
	 */
	public void updateSmsSign(Integer id, String smsSign) {
		if (StringUtils.isBlank(smsSign)) {
			throw new AppException("姓名不能为空");
		}
		repository.updateSmsSign(id, smsSign);
	}

    /**
     * 更新最后登录时间和登录次数
     * @param id
     */
	public void updateLastLogin(Integer id) {
	    repository.updateLastLogin(id);
    }
	
	/**
	 * 修改密码
	 * @param id  用户ID
	 * @param oldPassword 旧密码
	 * @param newPassword 新密码
	 */
	public void updatePassword(Integer id, String oldPassword, String newPassword) {
		if (StringUtils.isBlank(oldPassword)) {
			throw new AppException("旧密码不能为空");
		}

		if (StringUtils.isBlank(newPassword)) {
			throw new AppException("新密码不能为空");
		}

		Optional<User> optionalUser = findById(id);
		if (optionalUser.isPresent()) {
			User user = optionalUser.get();
			if(Hashs.hashEquals(user.getPassword(), oldPassword)) {
				String hashedPassword = Hashs.computeHash(newPassword);
				repository.updatePassword(id, hashedPassword);
			} else {
				throw new AppException("旧密码不正确");
			}
		} else {
			throw new AppException("用户不存在");
		}
	}

	/**
	 * 重置密码
	 * @param userName 用户名
	 * @param newPassword  新密码
	 */
	public void updatePassword(String userName, String newPassword) {
		AssertUtils.isNotBlank(userName);
		AssertUtils.isNotBlank(newPassword);

		User user = findByUserName(userName);
		if (user == null) {
			throw new AppException("用户不存在");
		} else {
			String hashedPassword = Hashs.computeHash(newPassword);
			repository.updatePassword(user.getId(), hashedPassword);
		}

	}

	public String getNextUserCode(Integer userType) {
		String maxCode;
		int maxInt;
		int codeSize;
		if (Constants.SENDER_TYPE_WEB.equals(userType)) {
			maxCode = repository.findMaxWebUserCode();
			codeSize = 7;
		} else {
			maxCode = repository.findMaxApiUserCode();
			codeSize = 3;
		}

		if (maxCode.length() != codeSize) {
			throw new AppException("获取账号失败");
		}

		maxInt = NumberUtils.toInt(maxCode);
		maxInt++;

		return StringUtils.leftPad(String.valueOf(maxInt), codeSize, '0');
	}
	
}
